package com.xfcycle.zebrabikegp.util;

import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.vise.baseble.ViseBle;
import com.vise.baseble.callback.IBleCallback;
import com.vise.baseble.callback.IConnectCallback;
import com.vise.baseble.callback.scan.IScanCallback;
import com.vise.baseble.callback.scan.RegularFilterScanCallback;
import com.vise.baseble.callback.scan.ScanCallback;
import com.vise.baseble.common.PropertyType;
import com.vise.baseble.core.BluetoothGattChannel;
import com.vise.baseble.core.DeviceMirror;
import com.vise.baseble.exception.BleException;
import com.vise.baseble.model.BluetoothLeDevice;
import com.vise.baseble.model.BluetoothLeDeviceStore;
import com.xfcycle.zebrabikegp.assist.BlueToothAssist;
import com.xfcycle.zebrabikegp.constant.BluetoothDeviceConstant;
import com.xfcycle.zebrabikegp.constant.ToastConstant;
import com.xfcycle.zebrabikegp.dto.BleTargetCharacteristicDTO;
import com.xfcycle.zebrabikegp.dto.ScanDeviceConditionsDTO;
import com.xfcycle.zebrabikegp.listener.BluetoothScanFindDeviceListener;

import java.util.Random;
import java.util.UUID;

/**
 * 连接蓝牙及相关操作工具类,该工具类为单例
 * Created by xFcycle on 2017/5/9.
 */

public class HandleDataByBLEUtil {

    private static HandleDataByBLEUtil instance;

    private BluetoothManager bluetoothManager;
    private BluetoothAdapter bluetoothAdapter;

    private BluetoothLeScanner bluetoothLeScanner;

    private static BluetoothGatt bluetoothGatt;

    //负责写的特征
    private BluetoothGattCharacteristic writeCharacteristic = null;

    //负责读的特征
    private BluetoothGattCharacteristic readCharacteristic = null;

    private BluetoothGattService service = null;

    public static ProgressDialog progress;

    private static Context mContext;

    //当前要搜索的蓝牙条件
    private ScanDeviceConditionsDTO scanDeviceConditionsDTO = null;

    //在生成 上一个 连接认证密码指令时，获得秘钥的序号 即秘钥本数组的下标
    private int preAuthKeyIndex = -1;

    private BluetoothScanFindDeviceListener bluetoothScanFindDeviceListener;

    private BleTargetCharacteristicDTO bleTargetCharacteristicDTO = null;

    private static final String TAG = "HandleDataByBLEUtil";

    //标识蓝牙查询是否开启
    private boolean scan_status = false;

    private int MESSAGE_AUTH = 11100;

    private Integer currentThreadCode = 0;

    //用来判断当前搜索类型是特定搜索还是广泛搜索，避免搜索线程开启时，scan_status相互影响导致出现过时异常
    private boolean isParticular = false;

    private static ViseBle viseBle;

    private DeviceMirror deviceMirror;

    private String deviceName;

    private HandleDataByBLEUtil(Context context) {
    }

    public static HandleDataByBLEUtil getInstance(Context context, int scanTime) {
        synchronized (HandleDataByBLEUtil.class) {
            if (null == instance) {
                instance = new HandleDataByBLEUtil(context);
            }
        }

        mContext = context;
        //初始化蓝牙BLE对象
        initViseBle(context, scanTime);

        return instance;
    }


    public static void deleteInstance() {
        disConnectDevice();
        instance = null;
    }

    /**
     * 初始化ViseBle
     */
    private static void initViseBle(Context context, int scanTime) {
        //蓝牙相关配置修改
        ViseBle.config()
                .setScanTimeout(scanTime)//扫描超时时间
                .setConnectTimeout(10 * 1000)//连接超时时间
                .setOperateTimeout(5 * 1000)//设置数据操作超时时间
                .setConnectRetryCount(3)//设置连接失败重试次数
                .setConnectRetryInterval(1000)//设置连接失败重试间隔时间
                .setOperateRetryCount(3)//设置数据操作失败重试次数
                .setOperateRetryInterval(1000)//设置数据操作失败重试间隔时间
                .setMaxConnectCount(3);//设置最大连接设备数量
        //蓝牙信息初始化，全局唯一，必须在应用初始化时调用
        viseBle = ViseBle.getInstance();
        viseBle.init(context);
    }

    /***********************************************************************************************/
    /**                                 开启蓝牙并搜索设备                                          **/
    /***********************************************************************************************/

    /**
     * 判断设备是否支持蓝牙BLE
     *
     * @return 为boolean类型 是否支持 true 支持 false 不支持
     */
    public boolean judgeDeviceSupportBle() {
        boolean supportBluetooth = false;

        // 判断蓝牙设备是否支持
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // 判断设备是否支持蓝牙BLE
            if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                supportBluetooth = true;
            }
        }

        return supportBluetooth;
    }

    /**
     * 打开蓝牙功能
     */
    public void startBluetooth() {
        //获取蓝牙管理器
        if (null == bluetoothManager) {
            bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
        }

        //获取蓝牙适配器
        if (null == bluetoothAdapter) {
            bluetoothAdapter = bluetoothManager.getAdapter();
        }

        //判断蓝牙是否开启，如果没有开启，跳转到蓝牙开启界面
        if (null == bluetoothAdapter || !bluetoothAdapter.isEnabled()) {
            Intent enableBTIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            mContext.startActivity(enableBTIntent);
        }
    }

    /**
     * 广域搜索蓝牙设备
     *
     * @param iScanCallback 搜索设备的回调
     */
    public void scanDevices(IScanCallback iScanCallback) {
        if (null == viseBle) {
            return;
        }
        viseBle.startScan(new ScanCallback(iScanCallback));
    }

    /**
     * 依靠正则表达式搜索蓝牙设备
     *
     * @param regularDeviceName 蓝牙名需要匹配的正则表达式
     * @param iScanCallback     搜索到设备的回调函数
     */
    public void scanRegularDevices(String regularDeviceName, IScanCallback iScanCallback) {
        viseBle.startScan(new RegularFilterScanCallback(iScanCallback).setRegularDeviceName(regularDeviceName));
    }

    /**
     * 停止搜索
     */
    public void stopScanDevice() {
        Log.i(TAG, "stopScanDevice: 停止搜索蓝牙设备");

        viseBle.stopScan(new ScanCallback(new IScanCallback() {
            @Override
            public void onDeviceFound(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
                Log.i(TAG, "onDeviceFound: 搜索到设备");
            }

            @Override
            public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
                Log.i(TAG, "onScanFinish: 搜索结束");
            }

            @Override
            public void onScanTimeout() {
                Log.i(TAG, "onScanTimeout: 搜索超时");
            }
        }));
    }

    /***********************************************************************************************/
    /**                          利用设备mac地址 或蓝牙名称 连接设备                                 **/
    /***********************************************************************************************/

    /**
     * 利用设备信息连接蓝牙
     *
     * @param bluetoothLeDevice 设备信息
     */
    public void connectDeviceByDevice(BluetoothLeDevice bluetoothLeDevice, IConnectCallback iConnectCallBack) {
        viseBle.connect(bluetoothLeDevice, iConnectCallBack);
    }

    /**
     * 根据MAC地址直接扫描并连接设备
     *
     * @param macAddress       设备mac地址
     * @param iConnectCallBack 连接回调函数
     */
    public void connectDeviceByMacWithoutScan(String macAddress, IConnectCallback iConnectCallBack) {
        viseBle.connectByMac(macAddress, iConnectCallBack);
    }

    /**
     * 根据MAC地址直接扫描并连接设备
     *
     * @param name             设备名称
     * @param iConnectCallback 连接回调函数
     */
    public void connectDeviceByNameWithoutScan(String name, IConnectCallback iConnectCallback) {
        this.deviceName = name;
        viseBle.connectByName(name, iConnectCallback);
    }

    /**
     * 断开连接
     */
    public static void disConnectDevice() {
        //断开连接
        viseBle.disconnect();
        //释放资源
        viseBle.clear();
    }

    /**
     * 绑定读写通道
     *
     * @param deviceMirror
     */
    public void bindWriteAndReadChannel(DeviceMirror deviceMirror) {
        //绑定写通道
        deviceMirror.bindChannel(new IBleCallback() {
            @Override
            public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {
                Log.i(TAG, "onSuccess: 写数据成功" + new String(data));
            }

            @Override
            public void onFailure(BleException exception) {
                Log.i(TAG, "onFailure: 绑定写失败");
            }
        }, new BluetoothGattChannel.Builder()
                .setBluetoothGatt(deviceMirror.getBluetoothGatt())
                .setPropertyType(PropertyType.PROPERTY_WRITE)
                .setServiceUUID(UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"))
                .setCharacteristicUUID(UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"))
                .builder());

        //利用Handle延迟0.5秒发送验证信息
        authHandle.sendEmptyMessageDelayed(MESSAGE_AUTH, 500);


        //绑定通知通道
        deviceMirror.bindChannel(new IBleCallback() {
            @Override
            public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {
                Log.i(TAG, "onSuccess: 绑定通知成功" + new String(data));

                setNotificationListener(bluetoothGattChannel);
            }

            @Override
            public void onFailure(BleException exception) {
                Log.i(TAG, "onFailure: 绑定通知失败");
            }
        }, new BluetoothGattChannel.Builder()
                .setBluetoothGatt(deviceMirror.getBluetoothGatt())
                .setPropertyType(PropertyType.PROPERTY_NOTIFY)
                .setServiceUUID(UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"))
                .setCharacteristicUUID(UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"))
                .builder());
        deviceMirror.registerNotify(false);

        this.deviceMirror = deviceMirror;
    }

    /**
     * 设置通知监听器
     *
     * @param bluetoothGattChannel
     */
    private void setNotificationListener(BluetoothGattChannel bluetoothGattChannel) {
        deviceMirror.setNotifyListener(bluetoothGattChannel.getGattInfoKey(), new IBleCallback() {
            @Override
            public void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice) {
                String result = new String(data);

                Log.i(TAG, "onSuccess: 监听器监听到的数据 " + result);
                sendDataByBroadcast(result);
            }

            @Override
            public void onFailure(BleException exception) {

            }
        });
    }
    /***********************************************************************************************/
    /**                             利用characteristic读写数据到设备中                              **/
    /***********************************************************************************************/
    private Handler authHandle = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //搜索到读写特征后，默认向设备发送一条认证信息，5秒内没有发送或认证失败会自动断开蓝牙连接
            String authCode = createAuthInstruct();
            Log.e(TAG, "handleMessage: ------------------------------------------------------- 发送验证密码 " + authCode);
            if (null == authCode) {
                return;
            }
            sendDataToDevice(authCode);
        }
    };

    /**
     * 写数据到设备中
     *
     * @param content 需要写的内容格式为 " 代号**|** " 竖杠不能忘
     */
    public void sendDataToDevice(String content) {
        if (!content.contains("a|") && !content.contains("c|")) {
            if (!BlueToothAssist.isConnect) {
                sendExceptionMessage(ToastConstant.DEVICE_DISCONNECT);
                return;
            }

            if (!BlueToothAssist.isUnlock) {
                sendExceptionMessage(ToastConstant.BIKE_LOCKED);
                return;
            }
        }

        if(!content.contains("AT+")) {
            content = "^" + content + "$";
        }

        if (null == content) {
            sendExceptionMessage(ToastConstant.NUll_INSTRUCT);
            return;
        } else if (null == deviceMirror) {
            sendExceptionMessage(ToastConstant.SEND_BLE_INSTRUCT_FAILURE_RECONNECT);
            return;
        }

        deviceMirror.writeData(content.getBytes());
    }

    /**
     * 接收到数据后 发送消息给前台页面展示
     *
     * @param result 从蓝牙设备获取到的数据
     */
    private void sendDataByBroadcast(String result) {
        //发送广播
        Intent broadcastInt = new Intent();
        broadcastInt.setAction(BluetoothDeviceConstant.GET_DATA_FROM_BLUETOOTH_BROADCAST_RECEIVER_ACTION);

        broadcastInt.putExtra("result", result);

        mContext.sendBroadcast(broadcastInt);
    }

    /**
     * 发送异常消息
     *
     * @param message 从蓝牙设备获取到的数据
     */
    private void sendExceptionMessage(String message) {
        //发送广播
        Intent broadcastInt = new Intent();
        broadcastInt.setAction(BluetoothDeviceConstant.EXCEPTION_MESSAGE_BROADCAST_RECEIVER_ACTION);
        broadcastInt.putExtra("exception", message);
        mContext.getApplicationContext().sendBroadcast(broadcastInt);
    }
    /***********************************************************************************************/
    /**                                        自定义方法                                          **/
    /***********************************************************************************************/

    private int tryCheckAmount = 1;

    /**
     * 连接上服务器后，发送验证指令，在这里做的是生成一个验证指令
     *
     * @return 返回验证指令的内容
     */
    public String createAuthInstruct() {

        if (null == deviceName) {
            Log.w(TAG, "createAuthInstruct: 搜索蓝牙设备名称为空");
            return null;
        }

        //获取当前连接设备的Base64编码地址
        String deviceBase64MacAddress = deviceName.split("_")[1];

        //生成0-1019中的一个随机数 获取密码本随机秘钥
        int sequence = 0;
        do {
            sequence = new Random().nextInt(1019);
        } while (sequence == preAuthKeyIndex);
        String auth_key = BluetoothDeviceConstant.AUTH_KEY_TAB[sequence];

        //对当前地址进行加密
        String authCodeStr = EncryptStringUtil.customBase64EncodeWithSalt(deviceBase64MacAddress, auth_key).replace("=", "");

        String authInstruct = "a|" + sequence + "," + authCodeStr;

        preAuthKeyIndex = sequence;
        tryCheckAmount++;
        Log.e(TAG, "createAuthInstruct: ------------------- " + authInstruct + "-------" + tryCheckAmount);

        return authInstruct;
    }
}
